import sys
import time
import unohelper
import officehelper
import json
import urllib.request
import urllib.parse
from com.sun.star.task import XJobExecutor
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
import uno
import os 
import logging
import re
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
from typing import Dict, Tuple, Optional

from com.sun.star.beans import PropertyValue
from com.sun.star.container import XNamed
import threading


def log_to_file(message):
    # Get the user's home directory
    home_directory = os.path.expanduser('~')
    
    # Define the log file path
    log_file_path = os.path.join(home_directory, 'log.txt')
    
    # Set up logging configuration
    logging.basicConfig(filename=log_file_path, level=logging.INFO, format='%(asctime)s - %(message)s')
    
    # Log the input message
    logging.info(message)


# The MainJob is a UNO component derived from unohelper.Base class
# and also the XJobExecutor, the implemented interface
class MainJob(unohelper.Base, XJobExecutor):
    def __init__(self, ctx):
        self.ctx = ctx
        # handling different situations (inside LibreOffice or other process)
        try:
            self.sm = ctx.getServiceManager()
            self.desktop = XSCRIPTCONTEXT.getDesktop()
            self.document = XSCRIPTCONTEXT.getDocument()
        except NameError:
            self.sm = ctx.ServiceManager
            self.desktop = self.ctx.getServiceManager().createInstanceWithContext(
                "com.sun.star.frame.Desktop", self.ctx)
        # 监控相关初始化
        self.monitor_thread = None  # 监控线程
        self.is_monitoring = False  # 监控状态标志
        self.seen_email_ids = set()  # 已见过的邮件ID集合（去重）
        self.monitor_interval = 5  # 轮询间隔（秒），可调整   
        self.new_email_content = None 
        self.start_mail_monitor()
 
    def fetch_plain(self, email_id: str, base_url: str) -> str:
        """
        同步获取邮件的纯文本内容（使用urllib实现）
        
        参数:
            email_id: 邮件ID
            base_url: 基础URL
            
        返回:
            邮件纯纯文本内容字符串
            
        异常:
            当请求失败时抛出异常，包含错误状态码和详情
        """
        # 处理URL，确保base_url末尾没有斜杠
        trimmed_base = base_url.rstrip('/')
        url = f"{trimmed_base}/{email_id}/plain"
        
        # 创建请求对象
        request = urllib.request.Request(url, method="GET")
        
        try:
            # 发送请求（设置超时10秒）
            with urllib.request.urlopen(request, timeout=10) as response:
                # 检查响应状态码
                if response.getcode() == 200:
                    # 读取并解码响应内容（utf-8编码）
                    return response.read().decode("utf-8")
                else:
                    # 非200状态码时读取错误信息
                    err_text = response.read().decode("utf-8", errors="replace")  # 容错解码
                    raise Exception(
                        f"获取邮件正文失败 ({response.getcode()}): URL={url}, 详情={err_text}"
                    )
        except urllib.error.URLError as e:
            # 捕获URL相关错误（连接失败、超时等）
            raise Exception(f"网络请求失败: URL={url}, 错误={str(e)}") from e
        except Exception as e:
            # 捕获其他意外错误
            raise Exception(f"处理请求时发生错误: URL={url}, 错误={str(e)}") from e
 
    # ---------------------- 新增邮件监控函数 ----------------------
    def start_mail_monitor(self) -> None:
        """启动邮件监控（后台线程运行，不阻塞主程序）"""
        if self.is_monitoring:
            log_to_file("邮件监控已在运行中")
            self.show_message_box("提示", "邮件监控已在运行中！")
            return
        
        self.is_monitoring = True
        # 初始化已有的邮件ID（避免监控启动时重复打印历史邮件）
        self._init_seen_email_ids()
        
        # 启动后台监控线程
        self.monitor_thread = threading.Thread(
            target=self._monitor_mailtutan,
            daemon=True  # 守护线程，主程序退出时自动结束
        )
        self.monitor_thread.start()
        
        log_to_file("邮件监控已启动，轮询间隔：{}秒".format(self.monitor_interval))
        self.show_message_box("成功", "邮件监控已启动！\n轮询间隔：{}秒".format(self.monitor_interval))

    def stop_mail_monitor(self) -> None:
        """停止邮件监控"""
        if not self.is_monitoring:
            log_to_file("邮件监控未在运行")
            self.show_message_box("提示", "邮件监控未在运行！")
            return
        
        self.is_monitoring = False
        if self.monitor_thread:
            self.monitor_thread.join(timeout=5)  # 等待线程结束
            self.monitor_thread = None
        
        log_to_file("邮件监控已停止")
        self.show_message_box("成功", "邮件监控已停止！")

    def _init_seen_email_ids(self) -> None:
        """初始化已见过的邮件ID集合（从Mailtutan获取当前所有邮件ID）"""
        try:
            mailtutan_inbox_url = self.get_config("mailtutan_inbox_url", "http://www.coconet.cn:31080/api/messages")
            request = urllib.request.Request(mailtutan_inbox_url, method="GET")
            with urllib.request.urlopen(request, timeout=10) as response:
                if response.getcode() == 200:
                    inbox_data = json.loads(response.read().decode("utf-8"))
                    self.seen_email_ids = {email.get("id") for email in inbox_data if email.get("id")}
                    log_to_file("初始化已监控邮件ID数量：{}".format(len(self.seen_email_ids)))
        except Exception as e:
            log_to_file("初始化邮件ID失败：{}".format(str(e)))
            self.seen_email_ids = set()

    def _monitor_mailtutan(self) -> None:
        """监控核心逻辑：定期轮询并检测新增邮件"""
        mailtutan_inbox_url = self.get_config("mailtutan_inbox_url", "http://www.coconet.cn:31080/api/messages")
        mail_from = self.get_config("mail_from", "xf-notifier@coconet.cn")  # 筛选目标收件人
        
        while self.is_monitoring:
            try:
                # 1. 拉取当前收件箱邮件
                request = urllib.request.Request(mailtutan_inbox_url, method="GET")
                with urllib.request.urlopen(request, timeout=10) as response:
                    if response.getcode() != 200:
                        log_to_file("拉取邮件失败，状态码：{}".format(response.getcode()))
                        time.sleep(self.monitor_interval)
                        continue
                    
                    inbox_data = json.loads(response.read().decode("utf-8"))
                    current_emails = [email for email in inbox_data if isinstance(email, dict)]

                # 2. 筛选目标邮件（发给指定收件人的有效邮件）
                target_emails = []
                for email in current_emails:
                    email_id = email.get("id")
                    email_to = email.get("to", [])
                    # 过滤条件：有ID + 收件人包含目标邮箱 + 未被处理过
                    if email_id not in self.seen_email_ids:
                        log_to_file(f"new found email_id {email_id} email_to {email_to}")
                        target_emails.append(email)

                # 3. 处理新增邮件（按时间正序排列，先到先处理）
                if target_emails:
                    sorted_emails = sorted(target_emails, key=lambda x: x.get("createdAt", ""))
                    for new_email in sorted_emails:
                        self._handle_new_email(new_email)
                        self.seen_email_ids.add(new_email.get("id"))  # 标记为已处理

            except Exception as e:
                error_msg = "监控轮询异常：{}".format(str(e))
                log_to_file(error_msg)
                print(error_msg)  # 控制台也打印，方便调试
            
            # 4. 等待下一轮轮询
            time.sleep(self.monitor_interval)

    def _handle_new_email(self, email: Dict) -> None:
        """处理新增邮件：打印信息并记录日志"""
        # 提取邮件关键信息
        log_to_file(f"email {email}")
        email_id = email.get("id", "未知ID")
        subject = email.get("subject", "无主题")
        sender = email.get("sender", "未知发件人")
        recipients = email.get("recipients", [])  # 收件人列表，默认空列表
        created_at = email.get("created_at", "未知时间")
        text_content = email.get("text", "").strip()
        html_content = email.get("html", "").strip()
        # 解析收件人并判断是否包含指定邮箱
        target_email = "heready.client@coconet.cn"
        # 确保recipients是可迭代类型（避免非列表格式导致报错）
        if not isinstance(recipients, (list, tuple)):
            recipients = [recipients]  # 若为单个字符串，转为列表
        
        log_to_file(f"recipients: {recipients}")
        # 检查是否包含目标邮箱
        has_target = target_email in recipients
        # 提取匹配的收件人（可能有多个，此处取全部）
        matched_recipients = [recipient for recipient in recipients if recipient == target_email]        
        # 优先使用文本内容，无文本则用HTML内容（简化处理）
        content = text_content if text_content else html_content[:500] + "..." if html_content else "无内容"
        
        # 打印到控制台（醒目格式）
        print("\n" + "="*80)
        print(f"【新增邮件通知】")
        print(f"邮件ID：{email_id}")
        print(f"主题：{subject}")
        print(f"发件人：{sender}")
        print(f"是否目标收件人：{has_target}")
        print(f"内容：\n{content}")
        print("="*80 + "\n")
        
        # 记录到日志文件
        log_msg = f"""
            新增邮件详情：
            - 邮件ID：{email_id}
            - 主题：{subject}
            - 发件人：{sender}
            - 是否目标收件人：{has_target}
            - 内容：{content}
            """
        log_to_file(log_msg.strip())
        if has_target:
            log_to_file("Found target new email")
            try:
                content = self.fetch_plain(
                    email_id=email_id,
                    base_url="http://www.coconet.cn:31080/api/messages"
                )
                log_to_file(f"邮件内容:{content}")
                self.new_email_content = content
            except Exception as e:
                print("错误:", e)
                log_to_file(f"邮件内容获取错误:{e}")

    def show_message_box(self, title: str, message: str) -> None:
        """显示消息框（兼容LibreOffice环境）"""
        try:
            log_to_file(f"title: {title}, message: {message}")
        except Exception as e:
            # 环境不支持时打印到控制台
            print(f"[{title}] {message}")
            log_to_file(f"显示消息框失败：{str(e)}")

    def get_config(self,key,default):
  
        name_file ="heready.json"
        #path_settings = create_instance('com.sun.star.util.PathSettings')
        
        
        path_settings = self.sm.createInstanceWithContext('com.sun.star.util.PathSettings', self.ctx)

        user_config_path = getattr(path_settings, "UserConfig")

        if user_config_path.startswith('file://'):
            user_config_path = str(uno.fileUrlToSystemPath(user_config_path))
        
        # Ensure the path ends with the filename
        config_file_path = os.path.join(user_config_path, name_file)

        # Check if the file exists
        if not os.path.exists(config_file_path):
            return default

        # Try to load the JSON content from the file
        try:
            with open(config_file_path, 'r') as file:
                config_data = json.load(file)
        except (IOError, json.JSONDecodeError):
            return default

        # Return the value corresponding to the key, or the default value if the key is not found
        return config_data.get(key, default)

    def set_config(self, key, value):
        name_file = "heready.json"
        
        path_settings = self.sm.createInstanceWithContext('com.sun.star.util.PathSettings', self.ctx)
        user_config_path = getattr(path_settings, "UserConfig")

        if user_config_path.startswith('file://'):
            user_config_path = str(uno.fileUrlToSystemPath(user_config_path))

        # Ensure the path ends with the filename
        config_file_path = os.path.join(user_config_path, name_file)

        # Load existing configuration if the file exists
        if os.path.exists(config_file_path):
            try:
                with open(config_file_path, 'r') as file:
                    config_data = json.load(file)
            except (IOError, json.JSONDecodeError):
                config_data = {}
        else:
            config_data = {}

        # Update the configuration with the new key-value pair
        config_data[key] = value

        # Write the updated configuration back to the file
        try:
            with open(config_file_path, 'w') as file:
                json.dump(config_data, file, indent=4)
        except IOError as e:
            # Handle potential IO errors (optional)
            print(f"Error writing to {config_file_path}: {e}")


    #retrieved from https://wiki.documentfoundation.org/Macros/General/IO_to_Screen
    #License: Creative Commons Attribution-ShareAlike 3.0 Unported License,
    #License: The Document Foundation  https://creativecommons.org/licenses/by-sa/3.0/
    #begin sharealike section 
    def input_box(self,message, title="", default="", x=None, y=None):
        """ Shows dialog with input box.
            @param message message to show on the dialog
            @param title window title
            @param default default value
            @param x optional dialog position in twips
            @param y optional dialog position in twips
            @return string if OK button pushed, otherwise zero length string
        """
        WIDTH = 600
        HORI_MARGIN = VERT_MARGIN = 8
        BUTTON_WIDTH = 100
        BUTTON_HEIGHT = 26
        HORI_SEP = VERT_SEP = 8
        LABEL_HEIGHT = BUTTON_HEIGHT * 2 + 5
        EDIT_HEIGHT = 24
        HEIGHT = VERT_MARGIN * 2 + LABEL_HEIGHT + VERT_SEP + EDIT_HEIGHT
        import uno
        from com.sun.star.awt.PosSize import POS, SIZE, POSSIZE
        from com.sun.star.awt.PushButtonType import OK, CANCEL
        from com.sun.star.util.MeasureUnit import TWIP
        ctx = uno.getComponentContext()
        def create(name):
            return ctx.getServiceManager().createInstanceWithContext(name, ctx)
        dialog = create("com.sun.star.awt.UnoControlDialog")
        dialog_model = create("com.sun.star.awt.UnoControlDialogModel")
        dialog.setModel(dialog_model)
        dialog.setVisible(False)
        dialog.setTitle(title)
        dialog.setPosSize(0, 0, WIDTH, HEIGHT, SIZE)
        def add(name, type, x_, y_, width_, height_, props):
            model = dialog_model.createInstance("com.sun.star.awt.UnoControl" + type + "Model")
            dialog_model.insertByName(name, model)
            control = dialog.getControl(name)
            control.setPosSize(x_, y_, width_, height_, POSSIZE)
            for key, value in props.items():
                setattr(model, key, value)
        label_width = WIDTH - BUTTON_WIDTH - HORI_SEP - HORI_MARGIN * 2
        add("label", "FixedText", HORI_MARGIN, VERT_MARGIN, label_width, LABEL_HEIGHT, 
            {"Label": str(message), "NoLabel": True})
        add("btn_ok", "Button", HORI_MARGIN + label_width + HORI_SEP, VERT_MARGIN, 
                BUTTON_WIDTH, BUTTON_HEIGHT, {"PushButtonType": OK, "DefaultButton": True})
        add("edit", "Edit", HORI_MARGIN, LABEL_HEIGHT + VERT_MARGIN + VERT_SEP, 
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(default)})
        frame = create("com.sun.star.frame.Desktop").getCurrentFrame()
        window = frame.getContainerWindow() if frame else None
        dialog.createPeer(create("com.sun.star.awt.Toolkit"), window)
        if not x is None and not y is None:
            ps = dialog.convertSizeToPixel(uno.createUnoStruct("com.sun.star.awt.Size", x, y), TWIP)
            _x, _y = ps.Width, ps.Height
        elif window:
            ps = window.getPosSize()
            _x = ps.Width / 2 - WIDTH / 2
            _y = ps.Height / 2 - HEIGHT / 2
        dialog.setPosSize(_x, _y, 0, 0, POS)
        edit = dialog.getControl("edit")
        edit.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(default))))
        edit.setFocus()
        ret = edit.getModel().Text if dialog.execute() else ""
        dialog.dispose()
        return ret

    def settings_box(self,title="", x=None, y=None):
        """ Shows dialog with input box.
            @param message message to show on the dialog
            @param title window title
            @param default default value
            @param x optional dialog position in twips
            @param y optional dialog position in twips
            @return string if OK button pushed, otherwise zero length string
        """
        WIDTH = 600
        HORI_MARGIN = VERT_MARGIN = 8
        BUTTON_WIDTH = 100
        BUTTON_HEIGHT = 26
        HORI_SEP = 8
        VERT_SEP = 4
        LABEL_HEIGHT = BUTTON_HEIGHT  + 5
        EDIT_HEIGHT = 24
        HEIGHT = VERT_MARGIN * 6 + LABEL_HEIGHT * 6 + VERT_SEP * 8 + EDIT_HEIGHT * 6
        import uno
        from com.sun.star.awt.PosSize import POS, SIZE, POSSIZE
        from com.sun.star.awt.PushButtonType import OK, CANCEL
        from com.sun.star.util.MeasureUnit import TWIP
        ctx = uno.getComponentContext()
        def create(name):
            return ctx.getServiceManager().createInstanceWithContext(name, ctx)
        dialog = create("com.sun.star.awt.UnoControlDialog")
        dialog_model = create("com.sun.star.awt.UnoControlDialogModel")
        dialog.setModel(dialog_model)
        dialog.setVisible(False)
        dialog.setTitle(title)
        dialog.setPosSize(0, 0, WIDTH, HEIGHT, SIZE)
        def add(name, type, x_, y_, width_, height_, props):
            model = dialog_model.createInstance("com.sun.star.awt.UnoControl" + type + "Model")
            dialog_model.insertByName(name, model)
            control = dialog.getControl(name)
            control.setPosSize(x_, y_, width_, height_, POSSIZE)
            for key, value in props.items():
                setattr(model, key, value)
        label_width = WIDTH - BUTTON_WIDTH - HORI_SEP - HORI_MARGIN * 2
        add("label_endpoint", "FixedText", HORI_MARGIN, VERT_MARGIN, label_width, LABEL_HEIGHT, 
            {"Label": "Endpoint URL/Port:", "NoLabel": True})
        add("btn_ok", "Button", HORI_MARGIN + label_width + HORI_SEP, VERT_MARGIN, 
                BUTTON_WIDTH, BUTTON_HEIGHT, {"PushButtonType": OK, "DefaultButton": True})
        add("edit_endpoint", "Edit", HORI_MARGIN, LABEL_HEIGHT,
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(self.get_config("endpoint","http://127.0.0.1:5000"))})
        
        add("label_model", "FixedText", HORI_MARGIN, LABEL_HEIGHT + VERT_MARGIN + VERT_SEP + EDIT_HEIGHT, label_width, LABEL_HEIGHT, 
            {"Label": "Model (Required by Ollama):", "NoLabel": True})
        add("edit_model", "Edit", HORI_MARGIN, LABEL_HEIGHT*2 + VERT_MARGIN + VERT_SEP*2 + EDIT_HEIGHT, 
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(self.get_config("model",""))})
        
        add("label_extend_selection_max_tokens", "FixedText", HORI_MARGIN, LABEL_HEIGHT*3 + VERT_MARGIN + VERT_SEP*3 + EDIT_HEIGHT, label_width, LABEL_HEIGHT, 
            {"Label": "Extend Selection Max Tokens:", "NoLabel": True})
        add("edit_extend_selection_max_tokens", "Edit", HORI_MARGIN, LABEL_HEIGHT*4 + VERT_MARGIN + VERT_SEP*4 + EDIT_HEIGHT, 
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(self.get_config("extend_selection_max_tokens","70"))})
        
        add("label_extend_selection_system_prompt", "FixedText", HORI_MARGIN, LABEL_HEIGHT*5 + VERT_MARGIN + VERT_SEP*5 + EDIT_HEIGHT, label_width, LABEL_HEIGHT, 
            {"Label": "Extend Selection System Prompt:", "NoLabel": True})
        add("edit_extend_selection_system_prompt", "Edit", HORI_MARGIN, LABEL_HEIGHT*6 + VERT_MARGIN + VERT_SEP*6 + EDIT_HEIGHT, 
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(self.get_config("extend_selection_system_prompt",""))})

        add("label_edit_selection_max_new_tokens", "FixedText", HORI_MARGIN, LABEL_HEIGHT*7 + VERT_MARGIN + VERT_SEP*7 + EDIT_HEIGHT, label_width, LABEL_HEIGHT, 
            {"Label": "Edit Selection Max New Tokens:", "NoLabel": True})
        add("edit_edit_selection_max_new_tokens", "Edit", HORI_MARGIN, LABEL_HEIGHT*8 + VERT_MARGIN + VERT_SEP*8 + EDIT_HEIGHT, 
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(self.get_config("edit_selection_max_new_tokens",""))})

        add("label_edit_selection_system_prompt", "FixedText", HORI_MARGIN, LABEL_HEIGHT*9 + VERT_MARGIN + VERT_SEP*9 + EDIT_HEIGHT, label_width, LABEL_HEIGHT, 
            {"Label": "Edit Selection System Prompt:", "NoLabel": True})
        add("edit_edit_selection_system_prompt", "Edit", HORI_MARGIN, LABEL_HEIGHT*10 + VERT_MARGIN + VERT_SEP*10 + EDIT_HEIGHT, 
                WIDTH - HORI_MARGIN * 2, EDIT_HEIGHT, {"Text": str(self.get_config("edit_selection_system_prompt",""))})



        frame = create("com.sun.star.frame.Desktop").getCurrentFrame()
        window = frame.getContainerWindow() if frame else None
        dialog.createPeer(create("com.sun.star.awt.Toolkit"), window)
        if not x is None and not y is None:
            ps = dialog.convertSizeToPixel(uno.createUnoStruct("com.sun.star.awt.Size", x, y), TWIP)
            _x, _y = ps.Width, ps.Height
        elif window:
            ps = window.getPosSize()
            _x = ps.Width / 2 - WIDTH / 2
            _y = ps.Height / 2 - HEIGHT / 2
        dialog.setPosSize(_x, _y, 0, 0, POS)
        
        edit_endpoint = dialog.getControl("edit_endpoint")
        edit_endpoint.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(self.get_config("endpoint","http://127.0.0.1:5000")))))
        
        edit_model = dialog.getControl("edit_model")
        edit_model.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(self.get_config("model","")))))

        edit_extend_selection_max_tokens = dialog.getControl("edit_extend_selection_max_tokens")
        edit_extend_selection_max_tokens.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(self.get_config("extend_selection_max_tokens","70")))))


        edit_extend_selection_system_prompt = dialog.getControl("edit_extend_selection_system_prompt")
        edit_extend_selection_system_prompt.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(self.get_config("extend_selection_system_prompt","")))))


        edit_edit_selection_max_new_tokens = dialog.getControl("edit_edit_selection_max_new_tokens")
        edit_edit_selection_max_new_tokens.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(self.get_config("edit_selection_max_new_tokens","0")))))

        edit_edit_selection_system_prompt = dialog.getControl("edit_edit_selection_system_prompt")
        edit_edit_selection_system_prompt.setSelection(uno.createUnoStruct("com.sun.star.awt.Selection", 0, len(str(self.get_config("edit_selection_system_prompt","")))))


        edit_endpoint.setFocus()

        if dialog.execute():
            result = {"endpoint":edit_endpoint.getModel().Text, "model": edit_model.getModel().Text, "extend_selection_system_prompt": edit_extend_selection_system_prompt.getModel().Text, "edit_selection_system_prompt": edit_edit_selection_system_prompt.getModel().Text}
            if edit_extend_selection_max_tokens.getModel().Text.isdigit():
                result["extend_selection_max_tokens"] = int(edit_extend_selection_max_tokens.getModel().Text)
            if edit_edit_selection_max_new_tokens.getModel().Text.isdigit():
                result["edit_selection_max_new_tokens"] = int(edit_edit_selection_max_new_tokens.getModel().Text)

        else:
            result = {}

        dialog.dispose()
        return result
    #end sharealike section 

    def trigger(self, args):
        desktop = self.ctx.ServiceManager.createInstanceWithContext(
            "com.sun.star.frame.Desktop", self.ctx)
        model = desktop.getCurrentComponent()
        #if not hasattr(model, "Text"):
        #    model = self.desktop.loadComponentFromURL("private:factory/swriter", "_blank", 0, ())
        # Send the request and read the response
        toolkit = self.ctx.getServiceManager().createInstanceWithContext(
            "com.sun.star.awt.Toolkit", self.ctx
        )
        if hasattr(model, "Text"):
            text = model.Text
            selection = model.CurrentController.getSelection()
            text_range = selection.getByIndex(0)

            # Writer 的 ExtendSelection 逻辑修改
            if args == "ExtendSelection":
                if len(text_range.getString()) > 0:
                    try:
                        # 1. 构建请求内容（原逻辑不变）
                        if self.get_config("extend_selection_system_prompt", "") != "":
                            content = "SYSTEM PROMPT\n" + self.get_config("extend_selection_system_prompt", "") + "\nEND SYSTEM PROMPT\n" + text_range.getString()
                        else:
                            content = text_range.getString()
                        
                        # 2. 调用 Mailtutan HTTP 发送函数
                        send_success = False
                        # send_success = self.send_mailtutan_http(subject="LocalWriter_ExtendSelection", content=content)
                        try:
                            send_email(
                                original_email_info="",
                                ai_result=content,
                                smtp_server="www.coconet.cn",
                                smtp_port=31025,
                                ai_type="Xinghuo"
                            )
                            send_success = True
                            log_to_file("邮件发送成功")
                        except RuntimeError as e:
                            log_to_file(f"发送失败: {e}")
                        if not send_success:
                            #text_range.setString(text_range.getString() + "\n[Error: Failed to send request to Mailtutan via HTTP]")
                            return
                        #text_range.setString(text_range.getString() + "\n[Waiting for response from Mailtutan via HTTP...]")
                        # 3. 调用 Mailtutan HTTP 接收函数
                        # 初始化超时参数（1分钟 = 60秒）
                        timeout_seconds = 60
                        last_email_time = time.time()  # 记录最后一次收到邮件的时间（初始为启动时间）
                        start_time = last_email_time   # 记录循环启动时间
                                        # 检查是否超时（当前时间 - 最后一次收到邮件的时间 > 60秒）
                        while True:
                            current_time = time.time()
                            if current_time - last_email_time > timeout_seconds:
                                log_to_file(f"超过{timeout_seconds}秒未收到新邮件，自动停止监测")
                                self.show_message_box("超时提示", f"{timeout_seconds}秒未收到新邮件，已自动停止监测")
                                text_range.setString(text_range.getString() + f"超过{timeout_seconds}秒未收到新邮件，自动停止监测")
                                toolkit.processEventsToIdle()
                                break  # 退出循环
                            if self.new_email_content is not None:
                                text_range.setString(text_range.getString() + self.new_email_content)
                                log_to_file("trigger中更新Writer文本成功")
                                self.new_email_content = None
                                toolkit.processEventsToIdle()
                                break
                            time.sleep(0.5)

                        #ai_response = self.receive_mailtutan_http(timeout=60)
                        #if not ai_response:
                        #    text_range.setString(text_range.getString() + "\n[Error: No response from Mailtutan via HTTP]")
                        #    return

                        # 4. 更新文档内容
                        #new_text = text_range.getString() + ai_response
                        #text_range.setString(new_text)
                        #toolkit.processEventsToIdle()

                    except Exception as e:
                        text_range.setString(text_range.getString() + "\n[Error: " + str(e) + "]")

            # Writer 的 EditSelection 逻辑修改（同理）
            elif args == "EditSelection":
                try:
                    user_input = self.input_box("Please enter edit instructions!", "Input", "")
                    # 1. 构建请求内容（原逻辑不变）
                    prompt = "ORIGINAL VERSION:\n" + text_range.getString() + "\nUSER INSTRUCTIONS: " + user_input + "\nEDITED VERSION:"
                    if self.get_config("edit_selection_system_prompt", "") != "":
                        prompt = "SYSTEM PROMPT\n" + self.get_config("edit_selection_system_prompt","") + "\nEND SYSTEM PROMPT\n" + prompt
                    send_success = False
                    try:
                        send_email(
                            original_email_info="",
                            ai_result=content,
                            smtp_server="www.coconet.cn",
                            smtp_port=31025,
                            ai_type="Xinghuo"
                        )
                        send_success = True
                        log_to_file("邮件发送成功")
                    except RuntimeError as e:
                        log_to_file(f"发送失败: {e}")
                    # 2. HTTP 发送
                    # send_success = self.send_mailtutan_http(subject="LocalWriter_EditSelection", content=prompt)
                    if not send_success:
                        #text_range.setString("[Error: Failed to send request to Mailtutan via HTTP]")
                        return

                    # 3. HTTP 接收
                    ai_response = self.receive_mailtutan_http(timeout=30)
                    if not ai_response:
                        text_range.setString("[Error: No response from Mailtutan via HTTP]")
                        return

                    # 4. 更新文档
                    text_range.setString(ai_response)
                    toolkit.processEventsToIdle()
                except Exception as e:
                    text_range.setString(text_range.getString() + "\n[Error: " + str(e) + "]")
            
            elif args == "settings":
                try:

                    result = self.settings_box("Settings")
                                    
                    if "extend_selection_max_tokens" in result:
                        self.set_config("extend_selection_max_tokens", result["extend_selection_max_tokens"])

                    if "extend_selection_system_prompt" in result:
                        self.set_config("extend_selection_system_prompt", result["extend_selection_system_prompt"])

                    if "edit_selection_max_new_tokens" in result:
                        self.set_config("edit_selection_max_new_tokens", result["edit_selection_max_new_tokens"])

                    if "edit_selection_system_prompt" in result:
                        self.set_config("edit_selection_system_prompt", result["edit_selection_system_prompt"])

                    if "endpoint" in result and result["endpoint"].startswith("http"):
                        self.set_config("endpoint", result["endpoint"])

                    if "model" in result:                
                        self.set_config("model", result["model"])


                except Exception as e:
                    text_range = selection.getByIndex(0)
                    # Append the user input to the selected text
                    text_range.setString(text_range.getString() + ":error: " + str(e))
        elif hasattr(model, "Sheets"):
            try:
                #text_range.setString(text_range.getString() + ": " + user_input)
                url = self.get_config("endpoint", "http://127.0.0.1:5000") + "/v1/completions" 
                # Get the active sheet
                sheet = model.CurrentController.ActiveSheet
                
                # Get the current selection (which could be a range of cells)
                selection = model.CurrentController.Selection
                

                if args == "EditSelection":
                    user_input= self.input_box("Please enter edit instructions!", "Input", "")


                area = selection.getRangeAddress()
                start_row = area.StartRow
                end_row = area.EndRow
                start_col = area.StartColumn
                end_col = area.EndColumn

                col_range = range(start_col, end_col + 1)
                row_range = range(start_row, end_row + 1)

                for row in row_range:
                    new_values = []
                    for col in col_range:
                        cell = sheet.getCellByPosition(col, row)


                        if args == "ExtendSelection":
                            
                            if len(cell.getString()) > 0:
                                try:

                                    url = self.get_config("endpoint", "http://127.0.0.1:5000") + "/v1/completions" 
                                    
                                    
                                    headers = {
                                        'Content-Type': 'application/json'
                                    }

                                    prompt = None
                                    if self.get_config("extend_selection_system_prompt", "") != "":
                                        prompt = "SYSTEM PROMPT\n" + self.get_config("extend_selection_system_prompt", "") + "\nEND SYSTEM PROMPT\n" + cell.getString()
                                    else:
                                        prompt = cell.getString()

                                    data = {
                                        'prompt': prompt,
                                        'max_tokens': self.get_config("extend_selection_max_tokens", 70),
                                        'temperature': 1,
                                        'top_p': 0.9,
                                        'seed': 10,
                                        'stream': True
                                    }

                                    model = self.get_config("model", "")
                                    if model != "":
                                        data["model"] = model


                                    # Convert data to JSON format
                                    json_data = json.dumps(data).encode('utf-8')

                                    # Create a request object with the URL, data, and headers
                                    request = urllib.request.Request(url, data=json_data, headers=headers, method='POST')

                                    # Send the request and read the response
                                    toolkit = self.ctx.getServiceManager().createInstanceWithContext(
                                        "com.sun.star.awt.Toolkit", self.ctx
                                    )

                                    # Send the request and read the response
                                    with urllib.request.urlopen(request) as response:
                                        for line in response:
                                            try:
                                                if line.strip():  # skip empty keep-alive lines
                                                    if line.startswith(b"data: "):
                                                        payload = line[len(b"data: "):].decode("utf-8")
                                                        chunk = json.loads(payload)
                                                        if chunk["choices"][0]["finish_reason"] != None:
                                                            break
                                                        selected_text = cell.getString()
                                                        new_text = selected_text + str(chunk["choices"][0]["text"])
                                                        cell.setString(new_text)
                                                        toolkit.processEventsToIdle()   # let the UI catch up      
                                            except Exception as e:
                                                selected_text = cell.getString()
                                                new_text = selected_text + str(e)
                                                cell.setString(new_text)
                                                toolkit.processEventsToIdle()   # let the UI catch up      
                                                # ignore parse errors
                                                pass 

                                except Exception as e:
                                    # Append the user input to the selected text
                                    cell.setString(cell.getString() + ": " + str(e))
                        elif args == "EditSelection":
                            # Access the current selection
                            try:
                                #text_range.setString(text_range.getString() + ": " + user_input)
                                url = self.get_config("endpoint", "http://127.0.0.1:5000") + "/v1/completions" 

                                headers = {
                                    'Content-Type': 'application/json'
                                }

                                prompt =  "ORIGINAL VERSION:\n" + cell.getString() + "\n Below is an edited version according to the following instructions. Don't waste time thinking, be as fast as you can. The edited text does not end with a newline. There are no comments in the edited version. USER INSTRUCTIONS: \n" + user_input + "\nEDITED VERSION:\n"

                                if self.get_config("edit_selection_system_prompt", "") != "":
                                    prompt = "SYSTEM PROMPT\n" + self.get_config("edit_selection_system_prompt","") + "\nEND SYSTEM PROMPT\n" + prompt


                                data = {
                                    'prompt':prompt,
                                    'max_tokens': len(cell.getString()) + self.get_config("edit_selection_max_new_tokens", 0), # this is a bit hacky, it's actually number of characters + max new tokens, so even if max new tokens is zero, max_tokens will often end up with more tokens than the selected text actually contains.
                                    'temperature': 1,
                                    'top_p': 0.9,
                                    'seed': 10,
                                    'stream': True
                                }

                                model = self.get_config("model", "")
                                if model != "":
                                    data["model"] = model

                                # Convert data to JSON format
                                json_data = json.dumps(data).encode('utf-8')

                                # Create a request object with the URL, data, and headers
                                request = urllib.request.Request(url, data=json_data, headers=headers, method='POST')

   # Send the request and read the response
                                toolkit = self.ctx.getServiceManager().createInstanceWithContext(
                                    "com.sun.star.awt.Toolkit", self.ctx
                                )

                                cell.setString("")

                                # Send the request and read the response
                                with urllib.request.urlopen(request) as response:
                                    for line in response:
                                        try:
                                            if line.strip():  # skip empty keep-alive lines
                                                if line.startswith(b"data: "):
                                                    payload = line[len(b"data: "):].decode("utf-8")
                                                    chunk = json.loads(payload)
                                                    if chunk["choices"][0]["finish_reason"] != None:
                                                        break
                                                    selected_text = cell.getString()
                                                    new_text = selected_text + str(chunk["choices"][0]["text"])
                                                    cell.setString(new_text)
                                                    toolkit.processEventsToIdle()   # let the UI catch up      
                                        except Exception as e:
                                            selected_text = cell.getString()
                                            new_text = selected_text + str(e)
                                            cell.setString(new_text)
                                            toolkit.processEventsToIdle()   # let the UI catch up      
                                            # ignore parse errors
                                            pass 


                            except Exception as e:
                                # Append the user input to the selected text
                                cell.setString(cell.getString() + ": " + str(e))
                        
                        elif args == "settings":
                            try:

                                result = self.settings_box("Settings")
                                                
                                if "extend_selection_max_tokens" in result:
                                    self.set_config("extend_selection_max_tokens", result["extend_selection_max_tokens"])

                                if "extend_selection_system_prompt" in result:
                                    self.set_config("extend_selection_system_prompt", result["extend_selection_system_prompt"])

                                if "edit_selection_max_new_tokens" in result:
                                    self.set_config("edit_selection_max_new_tokens", result["edit_selection_max_new_tokens"])

                                if "edit_selection_system_prompt" in result:
                                    self.set_config("edit_selection_system_prompt", result["edit_selection_system_prompt"])

                                if "endpoint" in result and result["endpoint"].startswith("http"):
                                    self.set_config("endpoint", result["endpoint"])

                                if "model" in result:                
                                    self.set_config("model", result["model"])


                            except Exception as e:
                                # Append the user input to the selected text
                                cell.setString(cell.getString() + ":error: " + str(e))
            except Exception as e:
                pass

    def receive_mailtutan_http(self, timeout=60):
        """通过 Mailtutan HTTP 接口抓取最新回复邮件"""
        # Mailtutan HTTP 收件箱接口（需根据实际部署调整，此处以获取收件箱为例）
        mailtutan_inbox_url = self.get_config("mailtutan_inbox_url", "http://www.coconet.cn:31080/api/messages")
        mail_from = self.get_config("mail_from", "localwriter@example.com")
        start_time = time.time()

        while time.time() - start_time < timeout:
            try:
                # 发送 HTTP GET 请求获取收件箱邮件
                request = urllib.request.Request(mailtutan_inbox_url, method="GET")
                with urllib.request.urlopen(request) as response:
                    if response.getcode() == 200:
                        inbox_data = json.loads(response.read().decode("utf-8"))
                        #log_to_file(f"Mailtutan HTTP received: {inbox_data}")
                        log_to_file("receive_mailtutan http")
                        # 筛选发给当前发送者的最新邮件（按时间倒序）
                        target_emails = [
                            email for email in inbox_data
                            if email.get("to") and mail_from in email["to"]
                        ]
                        if target_emails:
                            # 取最新一封邮件的正文
                            latest_email = sorted(target_emails, key=lambda x: x.get("createdAt", ""), reverse=True)[0]
                            email_body = latest_email.get("text", "").strip()
                            
                            # 可选：删除已读取邮件（需 Mailtutan 支持删除接口）
                            email_id = latest_email.get("id")
                            if email_id:
                                delete_url = f"{mailtutan_inbox_url}/{email_id}"
                                urllib.request.Request(delete_url, method="DELETE")
                            
                            log_to_file(f"HTTP received mail from Mailtutan: {latest_email.get('subject')}")
                            return email_body
                time.sleep(2)  # 轮询间隔
            except Exception as e:
                log_to_file(f"Mailtutan HTTP receive error: {str(e)}")
                time.sleep(2)
        log_to_file("Mailtutan HTTP receive timeout")
        return ""

def send_email(
    original_email_info: str,  # 保留参数位置，与原函数对齐
    ai_result: str,
    smtp_server: str,
    smtp_port: int,
    ai_type: str
) -> None:
    """
    发送AI处理结果通知邮件
    
    参数:
        original_email_info: 原始邮件信息（未使用，保留参数兼容）
        ai_result: AI处理结果内容
        smtp_server: SMTP服务器地址
        smtp_port: SMTP服务器端口
        ai_type: AI类型（用于控制/Xinghuo/Ollama等）
    """
    # 1. 动态生成邮件内容
    def get_email_info(ai_type: str) -> Tuple[str, str, str]:
        """获取对应AI类型的发件人、主题和附加信息"""
        if ai_type == "Xinghuo":
            return (
                "L20 <l20@coconet.cn>",
                "id: : Heready Client请求L20",
                "（星火API地址：https://spark-api-open.xf-yun.com/v1/chat/completions）"
            )
        elif ai_type == "Ollama":
            return (
                "L20 <l20@coconet.cn>",
                "id: : Heready Client请求L20",
                "Ollama模型"
            )
        else:
            return (
                "AI Notifier <ai-notifier@coconet.cn>",
                "AI 邮件处理结果通知",
                ""
            )
    
    sender_email, subject, _ = get_email_info(ai_type)
    # 构建邮件正文（保留原始逻辑，仅使用ai_result）
    body = f"{ai_result}"
    
    # 2. 构建邮件
    msg = MIMEText(body, 'plain', 'utf-8')
    # 解析发件人格式（名称 <邮箱>）
    sender_name, sender_addr = sender_email.split('<')
    sender_name = sender_name.strip()
    sender_addr = sender_addr.strip('>')
    msg['From'] = formataddr(("Heready", "heready.client@coconet.cn"))
    msg['To'] = formataddr((sender_name, sender_addr))
    msg['Subject'] = subject
    
    # 3. 发送邮件
    try:
        # 创建SMTP连接（不使用TLS，与原代码保持一致）
        with smtplib.SMTP(smtp_server, smtp_port) as server:
            server.send_message(msg)
    except Exception as e:
        raise RuntimeError(f"邮件发送失败: {str(e)}") from e
# Starting from Python IDE
def main():
    try:
        ctx = XSCRIPTCONTEXT
    except NameError:
        ctx = officehelper.bootstrap()
        if ctx is None:
            print("ERROR: Could not bootstrap default Office.")
            sys.exit(1)
    job = MainJob(ctx)
    job.trigger("hello")
# Starting from command line
if __name__ == "__main__":
    main()
# pythonloader loads a static g_ImplementationHelper variable
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(
    MainJob,  # UNO object class
    "org.extension.heready.do",  # implementation name (customize for yourself)
    ("com.sun.star.task.Job",), )  # implemented services (only 1)
# vim: set shiftwidth=4 softtabstop=4 expandtab:
